﻿// CTileManager
// © Ren Katims 2013

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
#endregion

namespace Casters.source.Managers
{
    /// <summary>
    /// TileManager - Handles the Update and Rendering of the current Battle Map
    /// </summary>
    public class CTileManager
    {
        private static CTileManager instance = null;
        static public CTileManager GetInstance()
        {
            if (instance == null)
                instance = new CTileManager();

            return instance;
        }

        private Texture2D m_TileMapTexture;

        public CTile[,] m_TileMap;

        public Vector2 m_vMapSize;
        Vector2 m_vTileSizePixels;
        Vector2 m_vNumTiles;

        Color m_AlphaColor;

        Vector2 m_vWorldOffset;
        Vector2 m_vTileEdgeOffset;

        float m_fZoomScale;

        public CTileManager()
        { 
        
        }

        /// <summary>
        /// Initialize the TileManager
        /// </summary>
        /// <param name="_vMapSize"> Vector2 containing the size of the map we are creating</param>
        /// <param name="_vTileSizePixels">Vector2 containing the size of an individual tile in pixels</param>
        /// <param name="_vNumTiles">Vector2 contatining the number of tiles in the map</param>
        /// <param name="_Alpha">Color to Alpha - currently does nothing</param>
        /// <param name="_TileMapTex">Texture2D that we are rendering from</param>
        public void Initialize(Vector2 _vMapSize, Vector2 _vTileSizePixels, Vector2 _vNumTiles, Color _Alpha, Texture2D _TileMapTex)
        {
            m_vMapSize = _vMapSize;
            m_vTileSizePixels = _vTileSizePixels;
            m_vNumTiles = _vNumTiles;
            m_AlphaColor = _Alpha;
            m_TileMapTexture = _TileMapTex;

            m_vWorldOffset = new Vector2(200, 400);
            m_vTileEdgeOffset = new Vector2(0, 40);

            m_TileMap = new CTile[(int)_vMapSize.X, (int)_vMapSize.Y];
        }

        public void Reset()
        {
            m_vMapSize = Vector2.Zero;
            m_vTileSizePixels = Vector2.Zero;
            m_vNumTiles = Vector2.Zero;
            m_AlphaColor = Color.White;
            m_TileMapTexture.Dispose();

            m_vWorldOffset = new Vector2(200, 400);
            m_vTileEdgeOffset = new Vector2(0, 40);

            m_TileMap = null;
        }

        /// <summary>
        /// Generates a random map for testing purposes
        /// </summary>
        public void CreateRandomMap()
        {
            Random rand = new Random();

            for (int y = 0; y < (int)m_vMapSize.Y; ++y)
            {
                for (int x = 0; x < (int)m_vMapSize.X; ++x)
                {  
                    m_TileMap[x, y] = new CTile(rand.Next(9), 0, rand.Next(4), new Vector2(m_vTileSizePixels.X * x, m_vTileSizePixels.Y * y));
                }
            }
        }

        /// <summary>
        /// Changes the color of a tile
        /// </summary>
        /// <param name="_tileCoords">Tile to change</param>
        /// <param name="_color">Color to change to</param>
        /// <returns>The old color</returns>
        public Color ChangeTileColor(Vector2 _tileCoords, Color _color)
        {
            Color oldColor = m_TileMap[(int)_tileCoords.X, (int)_tileCoords.Y].m_color;
            m_TileMap[(int)_tileCoords.X, (int)_tileCoords.Y].m_color = _color;
            return oldColor;
        }

        /// <summary>
        /// Marks the Tiles Blue saying their within moveable range
        /// 
        /// By: Gant Briseno
        /// </summary>
        /// <param name="_pos">Player's position on the tilemap</param>
        public void MarkTiles(Vector2 _pos, float _distance, Color _color, bool _moving = false)
        {
            for (float y = 0; y < m_vMapSize.Y; ++y)
            {
                for (float x = 0; x < m_vMapSize.X; ++x)
                {
                    if (Math.Abs((_pos.X - x)) + Math.Abs((_pos.Y - y)) < _distance)
                    {
                        m_TileMap[(int)x, (int)y].m_color = _color;
                        if(_moving)
                            m_TileMap[(int)x, (int)y].m_CanMoveTo = true;
                    }
                }
            }
        }

        /// <summary>
        /// Marks the Tiles based on what the player can see
        /// 
        /// By: Gant Briseno
        /// </summary>
        /// <param name="_pos">Player's position on the tilemap</param>
        public void MarkTilesLoS(Vector2 _pos, float _distance, Color _color, Vector2 _DirectionFacing, bool _attacking = false)
        {
            float dist;
            float newX;
            float newY;
            float dot;
            for (float y = 0; y < m_vMapSize.Y; ++y)
            {
                for (float x = 0; x < m_vMapSize.X; ++x)
                {
                    newX = (x - _pos.X);
                    newY = (y - _pos.Y);
                    dist =  Math.Abs(newX) + Math.Abs(newY);
                    dot = Vector2.Dot(new Vector2(newX, newY), _DirectionFacing);
                    if (dist < _distance && dot >= 0f)
                    {
                        m_TileMap[(int)x, (int)y].m_color = _color;
                        if (_attacking)
                            m_TileMap[(int)x, (int)y].m_CanAttack = true;
                    }
                }
            }
        }

        /// <summary>
        /// Resets all tile colors back to white
        /// 
        /// By: Gant Briseno
        /// </summary>
        public void ResetTileColor()
        {
            for (int y = 0; y < (int)m_vMapSize.Y; ++y)
            {
                for (int x = 0; x < (int)m_vMapSize.X; ++x)
                {
                    m_TileMap[x, y].m_color = Color.White;
                }
            }
        }

        /// <summary>
        /// Resets all the tiles back to a false state
        /// 
        /// By: Gant Briseno
        /// </summary>
        public void ResetTileCanMove()
        {
            for (int y = 0; y < (int)m_vMapSize.Y; ++y)
            {
                for (int x = 0; x < (int)m_vMapSize.X; ++x)
                {
                    m_TileMap[x, y].m_CanMoveTo = false;
                }
            }
        }

        /// <summary>
        /// Resets all the tiles back to a false state
        /// 
        /// By: Gant Briseno
        /// </summary>
        public void ResetTileCanAttack()
        {
            for (int y = 0; y < (int)m_vMapSize.Y; ++y)
            {
                for (int x = 0; x < (int)m_vMapSize.X; ++x)
                {
                    m_TileMap[x, y].m_CanAttack = false;
                }
            }
        }


        /// <summary>
        /// GetTileTex
        /// </summary>
        /// <param name="_ID">An ID value for the appropriate texture on the tilesheet</param>
        /// <returns>Rectangle that is the source rect for rendering</returns>
        public Rectangle GetTileTex(int _ID)
        {
            // Determine cell position
            int X = _ID / (int)m_vNumTiles.X;
            int Y = _ID % (int)m_vNumTiles.Y;

            // Return the appropriate RECT
            Rectangle toReturn = new Rectangle((int)m_vTileSizePixels.X * X, (int)m_vTileSizePixels.Y * Y,
                                               (int)m_vTileSizePixels.X, (int)m_vTileSizePixels.Y);

            return toReturn;
        }

        public Vector2 GetTileCenterpointPosition(Vector2 _vPosOnMap)
        {
            int height = m_TileMap[(int)_vPosOnMap.X, (int)_vPosOnMap.Y].m_nHeight;

            Vector2 toReturn = Vector2.Zero;

            toReturn.X = ((_vPosOnMap.X * (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.5f) + (_vPosOnMap.Y * (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.5f));
            toReturn.Y = ((_vPosOnMap.Y * (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.5f) - (_vPosOnMap.X * (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.5f));

            toReturn = toReturn + (m_vTileSizePixels * 0.5f);

            if (height > 0)
            {
                for (int i = 0; i < height - 1; ++i)
                    toReturn.Y = toReturn.Y - m_vTileEdgeOffset.Y;
            }

            return toReturn;
        }

        /// <summary>
        /// Checks to see which tile has been clicked
        /// </summary>
        /// <param name="mousePos">Position of the mouse cursor, formatted by the camera</param>
        public Vector2 ClickOnTiles(Vector2 mousePos)
        {
            bool tileFound = false;
            int height = 0;
            int clickedX = 0;
            int clickedY = 0;

            for (int y = 0; y < (int)m_vMapSize.Y; y++)
            {
                for (int x = (int)m_vMapSize.X - 1; x >= 0; --x)
                {
                    int tempX = (int)((((float)x * (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.5f) + ((float)y * (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.5f)));
                    int tempY = (int)((((float)y * (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.5f) - ((float)x * (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.5f)));

                    float zoom = CGame.GetInstance().m_Camera.m_fZoom;
                    Rectangle toCheck = new Rectangle((int)((tempX + (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.25f) * zoom), 
                        (int)(((tempY + (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.25f) - (int)(m_vTileEdgeOffset.Y) * (m_TileMap[x,y].m_nHeight - 1)) * zoom), 
                        (int)(m_vTileSizePixels.X * 0.5f * zoom), (int)(m_vTileSizePixels.Y * 0.5f * zoom));

                    if (toCheck.Contains(new Point((int)mousePos.X, (int)mousePos.Y)))
                    {
                        if (tileFound)
                        {
                            if (height < m_TileMap[x, y].m_nHeight)
                            {
                                clickedX = x;
                                clickedY = y;
                                height = m_TileMap[x, y].m_nHeight;
                            }
                        }
                        else
                        {
                            clickedX = x;
                            clickedY = y;
                            height = m_TileMap[x, y].m_nHeight;
                            tileFound = true;
                        }
                    }
                }
            }

            if (tileFound)
            {
                // Debug for showing what tile was found from this function
                //m_TileMap[clickedX, clickedY].m_color = Color.Black;
                return new Vector2(clickedX, clickedY);
            }
            else
                return new Vector2(-1f);
        }

        /// <summary>
        /// "Render" function - actually just hands off render information to the renderengine
        /// </summary>
        public void Render()
        {
            m_fZoomScale = CGame.GetInstance().m_Camera.m_fZoom;

            for (int y = 0; y < (int)m_vMapSize.Y; y++)
            {
                for (int x = (int)m_vMapSize.X - 1; x >= 0; --x)
                {
                    //int tempX = 0 
                    //int tempY = 0 

                    int tempX = (int)((((float)x * (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.5f) + ((float)y * (m_vTileSizePixels.X - m_vTileEdgeOffset.X) * 0.5f)));
                    int tempY = (int)((((float)y * (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.5f) - ((float)x * (m_vTileSizePixels.Y - m_vTileEdgeOffset.Y) * 0.5f)));

                    Rectangle toDraw = new Rectangle(tempX, tempY, (int)(m_vTileSizePixels.X * 0.5f), (int)(m_vTileSizePixels.Y * 0.5f));

                    if (CGame.GetInstance().m_Camera.CheckCulling(toDraw) == false)
                        continue;

                    // Handle Height
                    if (m_TileMap[x, y].m_nHeight > 0)
                    {
                        for (int i = 0; i < m_TileMap[x, y].m_nHeight - 1; ++i)
                        {
                            CRenderEngine.Draw(m_TileMapTexture, (new Vector2(tempX, tempY)) * m_fZoomScale, GetTileTex(m_TileMap[x, y].m_nTextureID), m_TileMap[x,y].m_color, 0f, Vector2.Zero, m_fZoomScale, SpriteEffects.None, 0f);
                            //_SpriteBatch.Draw(m_TileMapTexture, new Vector2(tempX, tempY), GetTileTex(m_TileMap[x, y].m_nTextureID), Color.White);
                            tempY = tempY - (int)(m_vTileEdgeOffset.Y);
                        }
                    }

                    CRenderEngine.Draw(m_TileMapTexture, (new Vector2(tempX, tempY)) * m_fZoomScale, GetTileTex(m_TileMap[x, y].m_nTextureID), m_TileMap[x, y].m_color, 0f, Vector2.Zero, m_fZoomScale, SpriteEffects.None, 0f);
                    //_SpriteBatch.Draw(m_TileMapTexture, new Vector2(tempX, tempY), GetTileTex(m_TileMap[x, y].m_nTextureID), Color.White);
                    //int p = 0;
                }
            }
        }
    }
}
